package org.example.util;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.x509.X509V3CertificateGenerator;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.security.auth.x500.X500Principal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.spec.ECParameterSpec;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Sm2Utils {
    public static void main(String[] args) {
        System.out.println(Integer.MAX_VALUE);
        System.out.println(NumberUtil.toBytes(1).length);
        System.out.println(Base64.encode(" "));
    }

    private static void test() {
        try {
            SM2 sm2 = SmUtil.sm2();
            PrivateKey privateKey = sm2.getPrivateKey();
            PublicKey publicKey = sm2.getPublicKey();
            X509Certificate certificate = genSm2Cert(privateKey, publicKey);

            Signature signature = Signature.getInstance(
                    GMObjectIdentifiers.sm2sign_with_sm3.toString(),
                    new BouncyCastleProvider()
            );
            signature.initSign(privateKey);
            byte[] plainText = "Hello world".getBytes(StandardCharsets.UTF_8);
            // 写入原文
            signature.update(plainText);
            // 计算签名值
//            byte[] sign = signature.sign();
            byte[] sign = sm2.sign(plainText);
            System.out.println("signature: "+ Base64.encode(sign));
            // 加密
            String signText = new JSONObject()
                    .set("text", new String(plainText))
                    .set("sign", Base64.encode(sign))
                    .toString();

            String secretText = sm2.encryptBase64(signText, KeyType.PublicKey);
            System.out.println("secret text: "+ secretText);

            // 验签
//            signature.initVerify(certificate.getPublicKey());
            // 写入待验签的原文
//            signature.update(plainText);

            // 验签
//            boolean verify = signature.verify(sign);
            String content = sm2.decryptStr(secretText, KeyType.PrivateKey);
            JSONObject contentJSON = JSONUtil.parseObj(content);
            String text = contentJSON.getStr("text");
            String signText1 = contentJSON.getStr("sign");
            boolean verify = sm2.verify(text.getBytes(StandardCharsets.UTF_8), Base64.decode(signText1));
            System.out.println("验签结果：" + verify);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void genSM2CertByRoot() {
        SimpleDateFormat format = new SimpleDateFormat("yyyy=MM-dd HH:mm:ss");
        BouncyCastleProvider bouncyCastleProvider = new BouncyCastleProvider();
        Security.addProvider(bouncyCastleProvider);
        String certFilename = "root"+new Date().getTime()/1000;
        String path = "sm2";
        String rootCertPath = path+certFilename+".cer";
        try {
            KeyPair kp = SecureUtil.generateKeyPair("SM2");
            System.out.println("公钥算法： "+kp.getPublic().getAlgorithm());
            BCECPrivateKey bcecPrivateKey = (BCECPrivateKey) kp.getPrivate();
            BCECPublicKey bcecPublicKey = (BCECPublicKey) kp.getPublic();
            X509Certificate rootCert = genSm2Cert(bcecPrivateKey, bcecPublicKey);
            IoUtil.write(FileUtil.getOutputStream(rootCertPath), true, rootCert.getEncoded());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void store() {
        try {
            KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
            char[] pass = "123456".toCharArray();
            final SM2 sm2 = SmUtil.sm2();
            PrivateKey privateKey = sm2.getPrivateKey();
            PublicKey publicKey = sm2.getPublicKey();
            X509Certificate certificate = genSm2Cert(privateKey, publicKey);
            SecretKey sk = KeyGenerator.getInstance("AES").generateKey();
            KeyStore.Entry entry = new KeyStore.SecretKeyEntry(sk);
            KeyStore.ProtectionParameter p = new KeyStore.PasswordProtection(pass);
//            keyStore.setEntry("entry", entry, p);
//            keyStore.setKeyEntry("pk",privateKey, pass, new Certificate[]{certificate});
            keyStore.setCertificateEntry("cert", certificate);
            keyStore.store(FileUtil.getOutputStream("store"), pass);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static X509Certificate genSm2Cert(PrivateKey privateKey, PublicKey publicKey) {
        final Provider BC = new BouncyCastleProvider();
        try {
            ContentSigner sigGen = new JcaContentSignerBuilder("SM3withSM2")
                    .setProvider(BC)
                    .build(privateKey);
            X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE)
                    .addRDN(BCStyle.C, "中国")
                    .addRDN(BCStyle.O, "上海")
                    .addRDN(BCStyle.ST, "上海")
                    .addRDN(BCStyle.L, "浦东");
            X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(
                    builder.build(),
                    BigInteger.valueOf(1),
                    new Date(System.currentTimeMillis() - 50 * 1000),
                    new Date(System.currentTimeMillis() + 3600 * 1000),
                    builder.build(),
                    publicKey
            );
            X509Certificate certificate = new JcaX509CertificateConverter()
                    .setProvider(BC)
                    .getCertificate(certGen.build(sigGen));
            return certificate;

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    private static void test3() {
        String content = "明文";
        final SM2 sm2 = SmUtil.sm2();
        BCECPrivateKey privateKey =(BCECPrivateKey) sm2.getPrivateKey();
        PublicKey publicKey = sm2.getPublicKey();
        ECParameterSpec params = privateKey.getParams();
        String sign = sm2.signHex(HexUtil.encodeHexStr(content));
        boolean verify = sm2.verifyHex(HexUtil.encodeHexStr(content), sign);
        System.out.println(verify);
    }

    private static void test2() {
        String text = "明文";
        KeyPair pair = SecureUtil.generateKeyPair("SM2");
        byte[] privateKey = pair.getPrivate().getEncoded();
        byte[] publicKey = pair.getPublic().getEncoded();

        SM2 sm2 = SmUtil.sm2(privateKey, publicKey);
        PrivateKey privateKey1 = sm2.getPrivateKey();
        // 公钥加密， 私钥解密
        String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey);
        String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey));

        System.out.println(encryptStr);
        System.out.printf(decryptStr);
    }

    private static void test1() {
        String text = "明文";
        SM2 sm2 = SmUtil.sm2();

        // 公钥加密，私钥解密
        String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey);
        String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey));
        System.out.println(encryptStr);
        System.out.println(decryptStr);
    }
}
